package fun.parser.ast;

import java.io.IOException;
import java.io.StringReader;
import java.util.Stack;

import fun.parser.LexerException;
import fun.parser.ast.node.AddExpression;
import fun.parser.ast.node.DivideExpression;
import fun.parser.ast.node.FunctionDefineExpression;
import fun.parser.ast.node.FunctionExpression;
import fun.parser.ast.node.MinusExpression;
import fun.parser.ast.node.ModExpression;
import fun.parser.ast.node.MultiplyExpression;
import fun.parser.ast.node.NumericExpression;
import fun.parser.ast.node.PowerExpression;
import fun.parser.ast.node.VariableAssignmentExpression;
import fun.parser.ast.node.VariableExpression;
import fun.parser.ast.node.base.AstNode;
import fun.parser.ast.node.base.Expression;
import fun.parser.lexer.Token;
import fun.parser.lexer.TokenType;
import fun.parser.postfix.FunctionNode;
import fun.parser.postfix.PostfixNode;
import fun.parser.postfix.PostfixParser;
import fun.parser.postfix.PostfixProgram;

public class AstParser {
    public Expression parse(String input) throws IOException, LexerException {
	PostfixParser postfix = new PostfixParser();
	postfix.setSource(new StringReader(input));
	PostfixProgram postfixProgram = postfix.parse();
	Expression expr = this.parse(postfixProgram);
	return expr;
    }

    public Expression parse(PostfixProgram pe) {
	Expression root = null;
	Expression first = null;
	Expression second = null;
	Stack<Expression> operands = new Stack<Expression>();
	Stack<PostfixNode> postfix = pe.getPostfix();
	while (false == postfix.empty()) {
	    PostfixNode next = postfix.pop();
	    // ������
	    if (true == next.isOperand()) {
		Expression operand = null;
		if (TokenType.IDENTIFIER == next.getTokenType()) {
		    // ����
		    operand = new VariableExpression(next.getToken());
		} else {
		    // ��ֵ
		    operand = new NumericExpression(next.getToken());
		}
		operands.push(operand);
	    }
	    // ����ʵ���ϵ��ɲ������һ��
	    else if (true == next.isFunction()) {
		FunctionNode fun = (FunctionNode) next;
		Expression[] args = new Expression[fun.getArgumentCount()];
		PostfixProgram[] pes = fun.getArguments();
		for (int i = 0; i < pes.length; i++)
		    args[i] = new AstParser().parse(pes[i]);
		operands.push(new FunctionExpression(next.getToken(), args));
	    }
	    // ������
	    else if (true == next.isOperator()) {
		second = operands.pop();
		first = operands.pop();
		Token token = next.getToken();
		switch (next.getTokenType()) {
		case ADD:
		    root = new AddExpression(token, first, second);
		    break;
		case MINUS:
		    root = new MinusExpression(token, first, second);
		    break;
		case MULTIPLY:
		    root = new MultiplyExpression(token, first, second);
		    break;
		case DIVIDE:
		    root = new DivideExpression(token, first, second);
		    break;
		case MOD:
		    root = new ModExpression(token, first, second);
		    break;
		case POWER:
		    root = new PowerExpression(token, first, second);
		    break;
		case ASSIGN:
		    /*
		     * ��� '=' ����������: 1. ��ʾ�Ա�����ֵ,�� "a = 2+1"; 2.
		     * ��ʾ������,�� "f(x) = x^2+3";
		     */
		    if (first instanceof VariableExpression)
			root = new VariableAssignmentExpression(token, (VariableExpression) first, second);
		    else if (first instanceof FunctionExpression) {
			// ������
			FunctionExpression fun = (FunctionExpression) first;
			Expression[] funArgs = fun.getArguments();
			VariableExpression[] args = new VariableExpression[funArgs.length];
			for (int i = 0; i < args.length; i++)
			    args[i] = (VariableExpression) funArgs[i];
			root = new FunctionDefineExpression(fun.getToken(), args, second);
		    } else {
			// ��Ӧ�ó����������������ض��д�����
			System.out.println("Error On: " + first);
		    }
		    break;
		}
		this.setParent(root, first, second);
		operands.push(root);
	    } else {
		System.err.println("Unkown node: " + next);
		return root;
	    }
	}
	if (null == root)
	    root = operands.pop();
	return root;
    }

    private void setParent(AstNode parent, AstNode... children) {
	if (null == children)
	    return;
	for (AstNode child : children)
	    child.setParent(parent);
    }
}
